package timer

import (
	"reflect"
	"time"

	"gitee.com/hasika/gotool/container"
	"gitee.com/hasika/gotool/logx/logdef"
)

type Type string
type Func func(args any)

type timerRegistry struct {
	TimerType   Type
	HandlerFunc Func
	RType       reflect.Type
}

type TimerManager struct {
	timersMap     map[string]*runtimeTimer
	timerList     []*runtimeTimer
	timerRegistry map[Type]*timerRegistry
	log           logdef.ILogger
}

func NewTimerManager(log logdef.ILogger) *TimerManager {
	return &TimerManager{
		timersMap:     make(map[string]*runtimeTimer),
		timerList:     make([]*runtimeTimer, 0, 16),
		timerRegistry: make(map[Type]*timerRegistry),
		log:           log,
	}
}

func (g *TimerManager) AddTimer(key string, timerType Type, delay time.Duration, data interface{}) {
	g.RemoveTimer(key)
	g.Infof(key, "add timer,type %s,data %+v", timerType, data)
	tm := newTimer(key, timerType, delay, data)
	g.timersMap[key] = tm
	g.timerList = append(g.timerList, tm)
}

func (g *TimerManager) RemoveTimer(key string) {
	oldTimer, ex := g.timersMap[key]
	if !ex {
		return
	}
	delete(g.timersMap, key)
	g.timerList = container.RemoveFromSlice(g.timerList, oldTimer)
	g.Infof(key, "timer removed ")
}

func (g *TimerManager) Update(dt time.Duration) {
	if len(g.timerList) == 0 {
		return
	}
	triggerList := make([]*runtimeTimer, 0, 2)
	for _, timer := range g.timerList {
		timer.Delay -= dt
		if timer.Delay <= 0 {
			triggerList = append(triggerList, timer)
		}
	}
	for _, tr := range triggerList {
		g.triggerTimer(tr)
	}
}

func (g *TimerManager) RegisterTimer(schema any, tpe Type, f Func) {
	info := g.timerRegistry[tpe]
	if info != nil {
		g.Errorf("", "timer type register failed,schema:%+v,type:%+v", schema, tpe)
		return
	}
	info = &timerRegistry{}
	info.TimerType = tpe
	info.HandlerFunc = f
	info.RType = reflect.TypeOf(schema)
	g.timerRegistry[tpe] = info
}

func (g *TimerManager) Stop() {
	g.timersMap = make(map[string]*runtimeTimer)
	g.timerList = make([]*runtimeTimer, 0, 16)
}

func (g *TimerManager) Infof(key string, format string, args ...any) {
	g.log.WithSkip(1).WithField("timer_key", key).Infof(format, args...)
}

func (g *TimerManager) Warnf(key string, format string, args ...any) {
	g.log.WithSkip(1).WithField("timer_key", key).Warnf(format, args...)
}

func (g *TimerManager) Errorf(key string, format string, args ...any) {
	g.log.WithSkip(1).WithField("timer_key", key).Errorf(format, args...)
}
